/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名	: 	 bsp_epittimer.c
作者	   : 左忠凯
版本	   : V1.0
描述	   : EPIT定时器驱动文件。
其他	   : 配置EPIT定时器，实现EPIT定时器中断处理函数
论坛 	   : www.wtmembed.com
日志	   : 初版V1.0 2019/1/5 左忠凯创建
***************************************************************/
#include "bsp_epittimer.h"
#include "bsp_int.h"
#include "bsp_led.h"

/*
 * @description		: 初始化EPIT定时器.
 *					  EPIT定时器是32位向下计数器,时钟源使用ipg=66Mhz		 
 * @param - frac	: 分频值，范围为0~4095，分别对应1~4096分频。
 * @param - value	: 倒计数值。
 * @return 			: 无
 */
void epit1_init(unsigned int frac, unsigned int value)
{
	if (frac > 0XFFF)
		frac = 0XFFF;

	EPIT1->CR = 0; /* 先清零CR寄存器 */

	/*
     * CR寄存器:
     * bit25:24 01 时钟源选择Peripheral clock=66MHz
     * bit15:4  frac 分频值
     * bit3:	1  当计数器到0的话从LR重新加载数值
     * bit2:	1  比较中断使能
     * bit1:    1  初始计数值来源于LR寄存器值
     * bit0:    0  先关闭EPIT1
     */
	EPIT1->CR = (1 << 24 | frac << 4 | 1 << 3 | 1 << 2 | 1 << 1);

	EPIT1->LR = value; /* 倒计数值 */
	EPIT1->CMPR = 0;	 /* 比较寄存器，当计数器值和此寄存器值相等的话就会产生中断 */

	/* 使能GIC中对应的中断 			*/
	GIC_EnableIRQ(EPIT1_IRQn);

	/* 注册中断服务函数 			*/
	system_register_irqhandler(EPIT1_IRQn, (system_irq_handler_t)epit1_irqhandler, NULL);

	EPIT1->CR |= 1 << 0; /* 使能EPIT1 */
}

/*
 * @description			: EPIT中断处理函数
 * @param				: 无
 * @return 				: 无
 */
void epit1_irqhandler(void)
{
	static unsigned char state = 0;

	state = !state;
	if (EPIT1->SR & (1 << 0)) /* 判断比较事件发生 */
	{
		led_switch(LED0, state); /* 定时器周期到，反转LED */
	}

	EPIT1->SR |= 1 << 0; /* 清除中断标志位 */
}

/* Leverage GPT1 to provide Systick */
void SystemSetupSystick(uint32_t tickRateHz, void *tickHandler, uint32_t intPriority)
{
	uint32_t clockFreq;
	uint32_t spllTmp;

	/* Install IRQ handler for GPT1 */
	system_register_irqhandler(GPT1_IRQn, (system_irq_handler_t)(uint32_t)tickHandler, NULL);

	/* Enable Systick all the time */
	CCM->CCGR1 |= CCM_CCGR1_CG10_MASK | CCM_CCGR1_CG11_MASK;

	GPT1->CR = GPT_CR_SWR_MASK;
	/* Wait reset finished. */
	while (GPT1->CR == GPT_CR_SWR_MASK)
	{
	}
	/* Use peripheral clock source IPG */
	GPT1->CR = GPT_CR_WAITEN_MASK | GPT_CR_STOPEN_MASK | GPT_CR_DOZEEN_MASK |
						 GPT_CR_DBGEN_MASK | GPT_CR_ENMOD_MASK | GPT_CR_CLKSRC(1UL);
	/* Set clock divider to 1 */
	GPT1->PR = 0;

	/* Get IPG clock*/
	/* Periph_clk2_clk ---> Periph_clk */
	if (CCM->CBCDR & CCM_CBCDR_PERIPH_CLK_SEL_MASK)
	{
		switch (CCM->CBCMR & CCM_CBCMR_PERIPH_CLK2_SEL_MASK)
		{
		/* Pll3_sw_clk ---> Periph_clk2_clk ---> Periph_clk */
		case CCM_CBCMR_PERIPH_CLK2_SEL(0U):
			clockFreq = (24000000UL * ((CCM_ANALOG->PLL_USB1 & CCM_ANALOG_PLL_USB1_DIV_SELECT_MASK) ? 22U : 20U));
			break;

		/* Osc_clk ---> Periph_clk2_clk ---> Periph_clk */
		case CCM_CBCMR_PERIPH_CLK2_SEL(1U):
			clockFreq = 24000000UL;
			break;

		case CCM_CBCMR_PERIPH_CLK2_SEL(2U):
		case CCM_CBCMR_PERIPH_CLK2_SEL(3U):
		default:
			clockFreq = 0U;
			break;
		}

		clockFreq /= (((CCM->CBCDR & CCM_CBCDR_PERIPH_CLK2_PODF_MASK) >> CCM_CBCDR_PERIPH_CLK2_PODF_SHIFT) + 1U);
	}
	/* Pll2_main_clk ---> Periph_clk */
	else
	{
		/* Get SYS PLL clock*/
		if (CCM_ANALOG->PLL_SYS & CCM_ANALOG_PLL_SYS_DIV_SELECT_MASK)
		{
			spllTmp = (24000000UL * 22UL + (uint64_t)(24000000UL) * (uint64_t)(CCM_ANALOG->PLL_SYS_NUM) / (uint64_t)(CCM_ANALOG->PLL_SYS_DENOM));
		}
		else
		{
			spllTmp = (24000000UL * 20UL + (uint64_t)(24000000UL) * (uint64_t)(CCM_ANALOG->PLL_SYS_NUM) / (uint64_t)(CCM_ANALOG->PLL_SYS_DENOM));
		}

		switch (CCM->CBCMR & CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK)
		{
		/* PLL2 ---> Pll2_main_clk ---> Periph_clk */
		case CCM_CBCMR_PRE_PERIPH_CLK_SEL(0U):
			clockFreq = spllTmp;
			break;

		/* PLL2 PFD2 ---> Pll2_main_clk ---> Periph_clk */
		case CCM_CBCMR_PRE_PERIPH_CLK_SEL(1U):
			clockFreq = ((uint64_t)spllTmp * 18) / ((CCM_ANALOG->PFD_528 & CCM_ANALOG_PFD_528_PFD2_FRAC_MASK) >> CCM_ANALOG_PFD_528_PFD2_FRAC_SHIFT);
			break;

		/* PLL2 PFD0 ---> Pll2_main_clk ---> Periph_clk */
		case CCM_CBCMR_PRE_PERIPH_CLK_SEL(2U):
			clockFreq = ((uint64_t)spllTmp * 18) / ((CCM_ANALOG->PFD_528 & CCM_ANALOG_PFD_528_PFD0_FRAC_MASK) >> CCM_ANALOG_PFD_528_PFD0_FRAC_SHIFT);
			break;

		/* PLL2 PFD2 divided(/2) ---> Pll2_main_clk ---> Periph_clk */
		case CCM_CBCMR_PRE_PERIPH_CLK_SEL(3U):
			clockFreq = ((((uint64_t)spllTmp * 18) / ((CCM_ANALOG->PFD_528 & CCM_ANALOG_PFD_528_PFD2_FRAC_MASK) >> CCM_ANALOG_PFD_528_PFD2_FRAC_SHIFT)) >> 1U);
			break;

		default:
			clockFreq = 0U;
			break;
		}
	}
	clockFreq /= (((CCM->CBCDR & CCM_CBCDR_AHB_PODF_MASK) >> CCM_CBCDR_AHB_PODF_SHIFT) + 1U);
	clockFreq /= (((CCM->CBCDR & CCM_CBCDR_IPG_PODF_MASK) >> CCM_CBCDR_IPG_PODF_SHIFT) + 1U);

	/* Set timeout value and enable interrupt */
	GPT1->OCR[0] = clockFreq / tickRateHz - 1UL;
	GPT1->IR = GPT_IR_OF1IE_MASK;

	/* Set interrupt priority */
	GIC_SetPriority(GPT1_IRQn, intPriority);
	/* Enable IRQ */
	GIC_EnableIRQ(GPT1_IRQn);

	/* Start GPT counter */
	GPT1->CR |= GPT_CR_EN_MASK;
}

void SystemClearSystickFlag(void)
{
	GPT1->SR = GPT_SR_OF1_MASK;
}
